package services;


import java.util.*;
import javax.jws.WebMethod;
import javax.jws.WebService;
import middlewareWeb.Address;
import middlewareWeb.FrontEndQueue;
import middlewareWeb.InfoRM;
import middlewareWeb.TaskDescriptor;
import middlewareWeb.TaskTYPE;

/**
 * Classe che modellizza il Front End che si occupa di gestire la replicazione
 * del contenuto delle code dei worker
 * @author myusy
 */
@WebService()
public class FrontEnd /*implements Observer*/{
    
    private static FrontEndQueue codaFE = new FrontEndQueue();
    private static Hashtable clocks = new Hashtable();
    private static Timer timer = new Timer();
    private static TimerTask task = new MyTask();
    private static boolean timer_avviato = false;
    /**
     * Metodo costruttore
    */
    public FrontEnd()
    {
        //codaFE.addObserver(this);
        if(!timer_avviato){
            timer.schedule(task, 10000, 10000);
            timer_avviato = true;
        }
    }
    /**
     * Metodo che effettua l'inserimento nella sua coda seguendo l'ordinamento vector clock
     * @param c vector clock sottoforma di String
     * @param id id compoenente
     * @param op numero di operazione
     * @param idtask id del task
     * @param type tipo di task
     * @param port numero di porta
     * @param ip indirizzo ip
     * @param comm tipo di comando
     * @param link link alla risorsa
     * @return il toString del vector clock aggiornato
     *
    */
    @WebMethod
    public synchronized String enqueueFE(String c, String id,String op,int idtask,Integer type, int port, String ip, String comm,String link)
    {
        Hashtable vectorClock = convert2Hashtable(c);
        if(clocks.isEmpty())
            initClocks();
        if (((Integer)vectorClock.get("F001"))<=((Integer)clocks.get("F001"))){
            clocks = max(vectorClock,clocks);
            clocks.put("F001", ((Integer)clocks.get("F001"))+1);
            InfoRM info = new InfoRM(id,op,new TaskDescriptor(idtask,TaskTYPE.values()[type],comm,link),new Address(port,ip),vectorClock);
            if (info != null) {
                if (!this.codaFE.getCoda().contains(info)){
                    this.codaFE.inserisci(info);
                    return clocks.toString();
                }
                return clocks.toString();
            }
            else
            {
                return "";
            }
        }else
            return "";
    }
    /**
     * metodo che inizializza il vector clock locale del manager
     */
    private void initClocks()
    {
        clocks.put("M001",new Integer(0));
        clocks.put("W001",new Integer(0));
        clocks.put("W002",new Integer(0));
        clocks.put("W003",new Integer(0));
        clocks.put("T001",new Integer(0));
        clocks.put("F001",new Integer(0));
    }

    /**
    *Metodo che opera il confronto tra due vector clock e restituisce il nuovo vector clock che conterrà il
     * massimo valore per ogni componente del vettore
     * @return Hashtable vector clock
     */
    private Hashtable max(Hashtable ricevuto, Hashtable locale){
            Hashtable hash = new Hashtable();

            Enumeration e = ricevuto.keys();
            String key = new String();
            while(e.hasMoreElements()){
                key = (String)e.nextElement();
                if(key.compareTo("F001")!=0){
                    if(((Integer)ricevuto.get(key))<((Integer)locale.get(key)))
                        hash.put(key, locale.get(key));
                    else
                        hash.put(key, ricevuto.get(key));
                }else
                    hash.put(key, locale.get(key));
            }
            return hash;
    }
/*
    @Override
    public synchronized void update(Observable o, Object arg) {

        if (((FrontEndQueue)o).getCoda().size()>0) {
        InfoRM info = ((FrontEndQueue)o).getCoda().firstElement();
        
        if (info != null) {
        int index = ((FrontEndQueue)o).getCoda().indexOf(info);
        boolean replicato = false;
        boolean replicato1 = false;
        boolean replicato2 = false;
        while(!(replicato = reply(info.getComponentId(),info.getOperation(),info.getTask().getID(),info.getTask().getType().ordinal(),info.getAddress().getPort(),info.getAddress().getIp(),info.getTask().getCommand(),info.getTask().getLink()))) {}
        while(!(replicato1 = reply1(info.getComponentId(),info.getOperation(),info.getTask().getID(),info.getTask().getType().ordinal(),info.getAddress().getPort(),info.getAddress().getIp(),info.getTask().getCommand(),info.getTask().getLink()))) {}
        while(!(replicato2 = reply2(info.getComponentId(),info.getOperation(),info.getTask().getID(),info.getTask().getType().ordinal(),info.getAddress().getPort(),info.getAddress().getIp(),info.getTask().getCommand(),info.getTask().getLink()))) {}
        ((FrontEndQueue)o).rimuovi(index);
        }
        }
    }
*/

    /**
    *Metodo che opera l'operazione inversa del toString(), ricostruisce l'hashtable
     * a partire da una stringa
     * @param clock stringa da convertire
     * @return Hashtable vector clock
     */
    public Hashtable convert2Hashtable(String clock){
        Hashtable hash = new Hashtable();

        String c = clock.replace("{", "");
        c = c.replace("}", "");
        c = c.replace(" ", "");

        String s[] = c.split(",");
        String a[]= new String[2];

        for(int i = 0;i<s.length;i++) {
            a = s[i].split("=");
            hash.put(a[0], Integer.parseInt(a[1]));
        }
        return hash;
    }
/**
 * Classe estende TimerTask, avvia la replica sui replica manager.
 * la replica avviene ogni 10 secondi, inviando metà del contenuto della coda del FE.
 * @author myusy
 */
    public static class MyTask extends TimerTask {

	    public synchronized void run() {
                int size = codaFE.getCoda().size();
	        if (size>0) {
                    int num_iterazioni = (int)(size * 0.5);
                    if(num_iterazioni == 0 ) num_iterazioni = size;
                    while(num_iterazioni!=0){
                        num_iterazioni --;
                        //size--;
                        InfoRM info = codaFE.getCoda().firstElement();

                        if (info != null) {
                            int index = codaFE.getCoda().indexOf(info);
                            boolean replicato = false;
                            boolean replicato1 = false;
                            boolean replicato2 = false;
                            while(!(replicato = reply(info.getComponentId(),info.getOperation(),info.getTask().getID(),info.getTask().getType().ordinal(),info.getAddress().getPort(),info.getAddress().getIp(),info.getTask().getCommand(),info.getTask().getLink()))) {}
                            while(!(replicato1 = reply1(info.getComponentId(),info.getOperation(),info.getTask().getID(),info.getTask().getType().ordinal(),info.getAddress().getPort(),info.getAddress().getIp(),info.getTask().getCommand(),info.getTask().getLink()))) {}
                            while(!(replicato2 = reply2(info.getComponentId(),info.getOperation(),info.getTask().getID(),info.getTask().getType().ordinal(),info.getAddress().getPort(),info.getAddress().getIp(),info.getTask().getCommand(),info.getTask().getLink()))) {}
                            codaFE.rimuovi(index);
                        }
                    }
                }
            }
    }
/**
     * Metodo che richiama il servizio offerto dal fornitore di servizi ReplicaManager
     * @return boolean: true se l'operazione è andata a buon fine, false altrimenti
     */
    private static boolean reply(java.lang.String arg0, java.lang.String arg1, int arg2, java.lang.Integer arg3, int arg4, java.lang.String arg5, java.lang.String arg6, java.lang.String arg7) {
        clientWSRM.ReplicaManagerService service = new clientWSRM.ReplicaManagerService();
        clientWSRM.ReplicaManager port = service.getReplicaManagerPort();
        return port.reply(arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7);
    }
   /**
     * Metodo che richiama il servizio offerto dal fornitore di servizi ReplicaManager1
     * @return boolean: true se l'operazione è andata a buon fine, false altrimenti
     */
    private static boolean reply1(java.lang.String arg0, java.lang.String arg1, int arg2, java.lang.Integer arg3, int arg4, java.lang.String arg5, java.lang.String arg6, java.lang.String arg7) {
        clientWSRM1.ReplicaManager1Service service_1 = new clientWSRM1.ReplicaManager1Service();
        clientWSRM1.ReplicaManager1 port = service_1.getReplicaManager1Port();
        return port.reply1(arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7);
    }
   /**
     * Metodo che richiama il servizio offerto dal fornitore di servizi ReplicaManager2
     * @return boolean: true se l'operazione è andata a buon fine, false altrimenti
     */
    private static boolean reply2(java.lang.String arg0, java.lang.String arg1, int arg2, java.lang.Integer arg3, int arg4, java.lang.String arg5, java.lang.String arg6, java.lang.String arg7) {
        clientWSRM2.ReplicaManager2Service service_2 = new clientWSRM2.ReplicaManager2Service();
        clientWSRM2.ReplicaManager2 port = service_2.getReplicaManager2Port();
        return port.reply2(arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7);
    }

}
